grammar plp3;

@header
{
	import java.lang.String;
}

@members
{
	final String TABULACION = "  ";
	final String TABINICIAL = "";
	TablaTipos tablaTipos = new TablaTipos();
	TablaSimbolos tablaSimbolos = new TablaSimbolos(tablaTipos);
	void mensajeError (String s)
	{
		System.err.println(s);
		System.exit (1);
	}
	
	String promociones (Integer inicio, Integer objetivo)
	{
		if (inicio == Tipo.BOOLEAN && objetivo == Tipo.DOUBLE)
		{
			return "conv.r8 \n";
		}
		if (inicio == Tipo.INT && objetivo == Tipo.DOUBLE)
		{
			return "conv.r8 \n";
		}
		if (inicio == Tipo.DOUBLE && objetivo == Tipo.INT)
		{
			return "conv.i4 \n";
		}
		return "";
	}
	int nE = 0;
	String nextLabel ()
	{
		return "L"+ (nE++);
	}

	final String header = ".assembly extern mscorlib {} \n .assembly 'Source' {} \n\n\n";
	
	final String classHeader = ".class 'single' extends [mscorlib]System.Object \n {/* Definicion del constructor */ \n .method public specialname rtspecialname instance void .ctor() cil managed \n { \n call instance void [mscorlib]System.Object::.ctor() \n ret \n }// fin .ctor \n";
	
	final String methodHeader = ".method static public void main () cil managed \n	{	\n .entrypoint	\n ";

	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}

}


@lexer::members
{
	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}
}

@rulecatch { 
  catch (RecognitionException re) { 
    reportError(re); 
    System.exit(1); 
  } 
} 

s : 
	clase [TABINICIAL + TABULACION]
		{System.out.println(header + $clase.trad);};

clase [String tab]
returns [String trad]: 
	CLASS 
	SINGLE 
	LLAVEI 
	{
		tablaSimbolos.push();
	}
	metodo [$tab + TABULACION]
	LLAVED
	{
		$trad = classHeader + $metodo.trad + $tab + "}	\n";
	};
	
metodo [String tab]
returns [String trad]: 
	PUBLIC 
	STATIC 
	VOID 
	MAIN 
	PARI 
	PARD 
	bloque [$tab + TABULACION, false, "", "", ""]
	{
		$trad = methodHeader + ".maxstack " + $bloque.maxStack + "\n" + " //Tabla de simbolos \n "+ tablaSimbolos.toString($tab) + $bloque.trad + $tab + "ret \n } " + $tab + "\n";
	};

declins [String tab, boolean breakPermitido, String etBucle, String etInc, String etFin]
returns [String trad, Integer maxStack]
@init {$trad = "";$maxStack = 0;}:   
	( 
	instr[$tab + TABULACION, $breakPermitido, $etBucle, $etInc, $etFin]
	{
		$trad += $instr.trad + '\n';
		$maxStack = Math.max($maxStack, $instr.maxStack);
	} 
	| 
	decl 
	{
		$trad += " // Declaracion de una variable \n";
		$maxStack = Math.max($maxStack, 0);
	}
	)*;

bloque [String tab, boolean breakPermitido, String etBucle, String etInc, String etFin]
returns [String trad, Integer maxStack] : 
	LLAVEI 
	{
		tablaSimbolos.push();
	}
	declins [$tab + TABULACION, $breakPermitido, $etBucle, $etInc, $etFin]
	{
		$trad = $declins.trad;
		$maxStack = $declins.maxStack;
		tablaSimbolos.pop();
	}
	LLAVED;

instr [String tab, boolean breakPermitido, String etBucle, String etInc, String etFin]
returns [String trad, Integer maxStack]
@init {int simbAux; boolean simbNegativo;} :
	bloque	[$tab + TABULACION, $breakPermitido, $etBucle, $etInc, $etFin]
	{
		$trad = $bloque.trad;
		$maxStack = $bloque.maxStack;
	}
	|
 	{
 		tablaSimbolos.push();
 	}
 	
 	IF PARI expr [$tab + TABULACION] PARD i1=instr[$tab + TABULACION, $breakPermitido, $etBucle, $etInc , $etFin] 
 	{
 		$etBucle = nextLabel();
 		$etFin = nextLabel();
 		if ($expr.tipo != Tipo.BOOLEAN)
 		{
 			mensajeError ("Error 5 (" + $IF.getLine() + "," + ($IF.getCharPositionInLine()+1) +
					"): " + "la expresion debe ser de tipo booleano en la instruccion '" + $IF.text + "'");
 		}
 		$trad = $expr.trad;
 		$trad += $tab + "ldc.i4 0 \n " ;
 		$trad += $tab + "beq " + $etBucle + " //Si el valor de la condicion es false (0), vamos a else/fin \n";
 		$trad += $i1.trad;
 		$trad += $tab + "br " + $etFin + " //Despues de IF, salimos del condicional \n";
 		$trad += $etBucle + " : // Venimos aqui si no se ejecuta el IF\n";
 		$maxStack = Math.max($expr.maxStack, $i1.maxStack); 
 	}
 	( ELSE i2=instr[$tab + TABULACION,false,"","", ""] 
	{
		$trad += $tab + " //Instrucciones del else \n";
		$trad += $i2.trad;

		$maxStack = Math.max($maxStack, $i2.maxStack);		
	}
	)?
	{
		$trad += $etFin + " : //Venimos aqui despues del IF \n";
		tablaSimbolos.pop();
	}
	|
	WHILE PARI expr [$tab + TABULACION] PARD 
	{
		tablaSimbolos.push();
		if ($expr.tipo != Tipo.BOOLEAN)
		{
 			mensajeError ("Error 5 (" + $WHILE.getLine() + "," + ($WHILE.getCharPositionInLine()+1) +
					"): " + "la expresion debe ser de tipo booleano en la instruccion '" + $WHILE.text + "'");
		}
		$etBucle = nextLabel();
		$etFin = nextLabel();
	}
	i1=instr[$tab + TABULACION, true, $etBucle , $etInc, $etFin]
	{
		$trad = $etBucle + " : \n";
		$trad += $expr.trad + " //Condicion del bucle \n";
		$trad += $tab + "ldc.i4 0 \n";
		$trad += $tab + "beq " + $etFin + "   //Si el valor de la condicion es false (0), vamos a fin \n";
		$trad += $i1.trad;
		$trad += "br " + $etBucle + " // Siguiente iteracion de while \n";
		$trad += $etFin + " : \n";
		
		$maxStack = Math.max($expr.maxStack+1, $i1.maxStack);
		
		tablaSimbolos.pop();
	}
	|
	FOREACH PARI VAR id1=ID IN id2=ID PARD 
	{
		tablaSimbolos.push();
		$etBucle = nextLabel();
		$etFin = nextLabel();
		int valorVector;
		int iterador = tablaSimbolos.crearIndice("");
		Simbolo simb = tablaSimbolos.busca($id2.text);

		if (simb == null)
		{
			mensajeError ("Error 2 (" + $id2.getLine() + "," + ($id2.getCharPositionInLine()+1) +
					"): simbolo '"+ $id2.text +"' no ha sido declarado");	
		}	
		if (!tablaTipos.isArray(simb.tipo))
		{
			mensajeError ("Error 11 (" + $id2.getLine() + "," + ($id2.getCharPositionInLine()+1) +
					"): " + "el identificador '" + $id2.text + "' no es de tipo array");
		}
		
		if (tablaTipos.getTipoBase(simb.tipo) == Tipo.DOUBLE)
		{
			valorVector = tablaSimbolos.crearIndice($id1.text, Tipo.DOUBLE);
		}
		else
		{
			valorVector =  tablaSimbolos.crearIndice($id1.text);
		}
	}
		i1=instr[$tab + TABULACION, true, $etBucle, $etInc, $etFin]
	{
		$trad = $tab + "ldc.i4 -1 \n";
		$trad += $tab + "stloc " + iterador + " //Inicializacion de la variable que controla el array del foreach \n";
		
		$trad += $etBucle + " : //Etiqueta de inicio del foreach \n";
		$trad += $tab + "ldloc " + iterador + "\n";
		$trad += $tab + "ldc.i4 1 \n";
		$trad += $tab + "add \n";
		$trad += $tab + "dup \n";
		$trad += $tab + "stloc " + iterador + " //Almacenamos el iterador para posterior uso \n";
		$trad += $tab + "ldc.i4 " + tablaTipos.getTamanyo(simb.tipo);
		$trad += $tab + "beq " + $etFin + " //Salto a fin si fin de vector \n";
		$trad += $tab + "ldloc " + tablaSimbolos.getPos($id2.text) + " //Cargamos el array \n";
		$trad += $tab + "ldloc " + iterador + " //Posicion del array (iterador)\n";
		if (tablaTipos.getTipoBase(simb.tipo) == Tipo.DOUBLE)
		{
			$trad += $tab + "ldelem.r8 \n";
		}
		else
		{
			$trad += $tab + "ldelem.i4 \n";
		}
		
		$trad += $tab + "stloc " + valorVector + " //Asignamos al valorVector \n";
		
		$trad += $i1.trad;
		$trad += $tab + "br " + $etBucle + "\n";
		$trad += $etFin + " : //Fin del foreach \n";
		
		$maxStack = Math.max($i1.maxStack, 3);
		
		tablaSimbolos.pop();
	}
	
	|
	
	FOR PARI INT ID
	{
		tablaSimbolos.push();

		simbAux = tablaSimbolos.crearIndice($ID.text);
		simbNegativo = false;
	}
	ASIG e1=expr[$tab + TABULACION] 
	{
		if ($e1.tipo != Tipo.INT && $e1.tipo != Tipo.DOUBLE)
		{
			mensajeError ("Error 6 (" + $ASIG.getLine() + "," + ($ASIG.getCharPositionInLine()+1) +
					"): " + "tipos incompatibles en la instruccion de asignacion");	
		}
	}
	 TO e2=expr[$tab + TABULACION] 
	 {
	 	if ($e2.tipo == Tipo.BOOLEAN)
		{
			mensajeError ("Error 17 (" + $TO.getLine() + "," + ($TO.getCharPositionInLine()+1) +
					"): " + "la expresion debe ser de tipo numerico");	
		}
	 }
	 STEP 
	(ADDOP
	{
		if ($ADDOP.text.equals("-"))
		{
			simbNegativo = true;
		}
	}
	)? ENTERO PARD 
	{
		$etBucle = nextLabel();
		$etFin = nextLabel();
	}
	i1=instr[$tab + TABULACION, true, $etBucle, $etInc, $etFin]
	{
		String etAux = nextLabel();
		
		$trad = $e1.trad;
		if ($e1.tipo == Tipo.DOUBLE)
		{
			$trad += $tab + "conv.i4 //Convertimos inicializacion a entero porque era double \n";
		}
		$trad += "stloc " + simbAux + " //Cargamos la inicializacion en la variable \n";		
		$trad += "br " + etAux + " //Primera iteracion, saltamos el incremento \n";
		
		$trad += $etBucle + " : //Incremento, condicion \n";
		$trad += "ldloc " + simbAux + " \n";
		$trad += "ldc.i4 " + $ENTERO.text + " //Valor del incremento \n";
		if (simbNegativo)
		{
			$trad += "sub \n";
		}
		else
		{
			$trad += "add \n";
		} 
		$trad += "stloc " + simbAux + "\n";
		$trad += etAux + " : //Etiqueta auxiliar para la primera iteracion \n";
		
		$trad += "ldloc " + simbAux + " //Recuperamos el valor del iterador \n";
		$trad += promociones(tablaSimbolos.busca(simbAux).tipo, $e2.tipo) + " //promocion del iterador \n";
		$trad += $e2.trad;
		if (simbNegativo)
		{
			$trad += "blt " + $etFin + " //Decrementamos el for, si es menor salimos \n";
		}
		else
		{
			$trad += "bgt " + $etFin + " //Incrementamos el for, si es mayor salimos \n";
		}
		
		$trad += $i1.trad;
		
		$trad += "br " + etBucle + " //Fin de bucle, siguiente iteracion \n";
	
	 	$trad += $etFin + " : //Fin de bucle\n";
	 	
	 	$maxStack = Math.max($e1.maxStack+1, Math.max($e2.maxStack+1, $i1.maxStack));
		tablaSimbolos.pop();
	}
	|	
	BREAK PYC
	{
		if (breakPermitido)
		{
			$trad = "br " + $etFin + " // Salida del bucle por BREAK \n";	
		}
	 	else
	 	{
	 		mensajeError ("Error 16 (" + $BREAK.getLine() + "," + ($BREAK.getCharPositionInLine()+1) +
				"): instruccion '" + $BREAK.text + "' no permitida fuera de un bucle");	
	 		//Error 16 (fila,columna): instruccion 'lexema' no permitida fuera de un bucle
	 	}
	 	$maxStack = 0;
	}
	|
	CONTINUE PYC
	{
		if (breakPermitido)
		{
			$trad = "br " + $etBucle + " // Iteracion por CONTINUE \n";	
		}
	 	else
	 	{
	 		mensajeError ("Error 16 (" + $CONTINUE.getLine() + "," + ($CONTINUE.getCharPositionInLine()+1) +
				"): instruccion '" + $CONTINUE.text + "' no permitida fuera de un bucle");
	 		//Error 16 (fila,columna): instruccion 'lexema' no permitida fuera de un bucle
	 	}
	 	$maxStack = 0;
	}
	|	
	ref[$tab + TABULACION] cambio [$tab + TABULACION, $ref.tipo, $ref.var, $ref.accesoArray, $ref.maxStack]
	{
		$trad = $cambio.trad;
		$maxStack = Math.max($cambio.maxStack+1, $ref.maxStack);
	}
	|	
	ID
	{
		Simbolo vect = tablaSimbolos.busca($ID.text);
		
		if (vect == null)
		{
			mensajeError ("Error 2 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): " + "simbolo '" + $ID.text + "' no ha sido declarado");		

		}
		if (vect.tipoSimbolo == Simbolo.INDICE)
		{
			mensajeError("Modificando indice");
		}
		if (!tablaTipos.isArray(vect.tipo))
		{
			mensajeError ("Error 11 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): " + "el identificador '" + $ID.text + "' no es de tipo array");
		}
		vect.inicializado = true;
	}
	ASIG NEW tiposimple CORI dims[$ID.text] CORD PYC 
	{
		int tipo = tablaTipos.desRef($dims.tipo);

		
		if (tablaTipos.isArray(tipo))
		{
			mensajeError ("Error 10 (" + $CORD.getLine() + "," + ($CORD.getCharPositionInLine()+1) +
					"): " + "numero de dimensiones incorrecto");
		}
	
		$trad = "ldc.i4 " + $dims.tam + "\n //Reserva de memoria para el array " + $instr.text + " \n";
		$trad += "newarr ";
		
		if ($tiposimple.tipo != tipo)
		{
			mensajeError ("Error 14 (" + $tiposimple.cc + "," + ($tiposimple.cl) +
					"): " + "tipo '" + $tiposimple.text + "' incompatible con la declaracion");
 
		}
		
		if (tipo == Tipo.INT || tipo == Tipo.BOOLEAN)
		{
			$trad += "[mscorlib]System.Int32 \n";
		}
		else
		{
			$trad += "[mscorlib]System.Double \n";
		}
		$trad += "stloc " + tablaSimbolos.getPos($ID.text) + " \n";
		$maxStack = 1;
	}
	| 
	WRITELINE PARI expr[$tab + TABULACION] PARD PYC
	{
		$trad = $expr.trad;
		$trad += $tab;
		if ($expr.tipo == Tipo.BOOLEAN)
		{
			$trad += "call void [mscorlib]System.Console::WriteLine(bool) \n";
		}
		if ($expr.tipo == Tipo.INT)
		{
			$trad += "call void [mscorlib]System.Console::WriteLine(int32) \n";
		}
		if ($expr.tipo == Tipo.DOUBLE)
		{
			$trad += "call void [mscorlib]System.Console::WriteLine(float64) \n";
		}
		$maxStack = $expr.maxStack;
	}
	;


dims [String var]
returns [Integer tam, Integer tipo] 
@init {$tam = 1;}: 
	e1=ENTERO 
	{
		$tipo = tablaSimbolos.busca(var).tipo;
		
		if (Integer.parseInt($e1.text)<1)
		{
			mensajeError ("Error 8 (" + $e1.getLine() + "," + ($e1.getCharPositionInLine()+1) +
					"): " + "tamanyo incorrecto");
		}
		
		tablaTipos.get($tipo).setTamanyo(Integer.parseInt($e1.text));
		$tam*=Integer.parseInt($e1.text);
	}
	(COMA e2=ENTERO
	{
		$tipo = tablaTipos.desRef($tipo);
		
		if (Integer.parseInt($e2.text) < 1)
		{
			mensajeError ("Error 8 (" + $e2.getLine() + "," + ($e2.getCharPositionInLine()+1) +
					"): " + "tamanyo incorrecto");
			//mensajeError("tamanyo incorrecto 2");	
		}
		
		if (!tablaTipos.isArray($tipo))
		{
			mensajeError ("Error 10 (" + $COMA.getLine() + "," + ($COMA.getCharPositionInLine()+1) +
					"): " + "numero de dimensiones incorrecto");
		}
		
		tablaTipos.get($tipo).setTamanyo(Integer.parseInt($e2.text));
		$tam*=Integer.parseInt($e2.text);
	}
	)*
	;

cambio  [String tab, Integer tipo, Integer var, String accesoArray, int maxStackRef]
returns [String trad, Integer maxStack] : 
	ASIG expr[$tab+TABULACION] PYC
	{
		int newTipo = OperaTipos.opAsig(tipo, $expr.tipo);
		if (newTipo == Tipo.ERROR)
		{
			mensajeError ("Error 6 (" + $ASIG.getLine() + "," + ($ASIG.getCharPositionInLine()+1) +
					"): " + "tipos incompatibles en la instruccion de asignacion");
			//Error 6 (fila,columna): tipos incompatibles en la instruccion de asignacion
		}
		
		if (tablaSimbolos.busca(var).tipoSimbolo == Simbolo.INDICE)
		{
			mensajeError ("Error 15 (" + $ASIG.getLine() + "," + ($ASIG.getCharPositionInLine()+1) +
					"): la variable que se intenta modificar es una variable indice");
		}
	
		if (accesoArray.equals(""))
		{
			$trad = $expr.trad + $tab + " //Valor de asignacion \n";
			$trad += $tab + promociones($expr.tipo, newTipo) + " //Promocion de asig \n";
			$trad += $tab + "stloc " + $var + "\n";
			$maxStack = $expr.maxStack;
		}
		else
		{
			$trad = $tab + "ldloc " + $var + $tab + " //Array en el que vamos a asignar \n";
			$trad += $accesoArray + $tab + " //Posicion del array \n";
			$trad += $expr.trad + $tab + " //Valor que asignamos al array \n";
			$trad += $tab + promociones($expr.tipo, newTipo) + " //Promocion de asig \n";
			if (tipo == Tipo.INT || tipo == Tipo.BOOLEAN)
			{
				$trad += $tab + "stelem.i4 //Cargamos el entero en el array \n";
			}
			else
			{
				$trad += $tab + "stelem.r8 //Cargamos el double en el array \n";
			}
			$maxStack = 2 + $expr.maxStack;
		}
	} 
	|
	PUNTO READLINE PYC
	{
		if (tablaSimbolos.busca(var).tipoSimbolo == Simbolo.INDICE)
		{
			mensajeError ("Error 15 (" + $READLINE.getLine() + "," + ($READLINE.getCharPositionInLine()+1) +
					"): la variable que se intenta modificar es una variable indice");
		}
		
		if (	(tipo == Tipo.INT && !$READLINE.text.equals("int.Parse(System.Console.ReadLine())")) ||
			(tipo == Tipo.DOUBLE && !$READLINE.text.equals("double.Parse(System.Console.ReadLine())")) ||
			(tipo == Tipo.BOOLEAN && !$READLINE.text.equals("bool.Parse(System.Console.ReadLine())")))
		{
			mensajeError ("Error 7 (" + $READLINE.getLine() + "," + ($READLINE.getCharPositionInLine()+1) +
					"): " + "tipos incompatibles en la instruccion de lectura");
		}
	
		if (accesoArray.equals(""))
		{
			if ($tipo == Tipo.DOUBLE)
			{
				$trad = $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call float64 [mscorlib]System.Double::Parse(string)\n";
			}
			else if ($tipo == Tipo.INT)
			{
				$trad = $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call int32 [mscorlib]System.Int32::Parse(string) \n";
			}
			else
			{
				$trad = $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call bool [mscorlib]System.Boolean::Parse(string) \n ";
			}
			$trad += $tab + "stloc " + $var + "\n";
			$maxStack = 1;
		}
		else
		{
			$trad = $tab + "ldloc " + $var + " //Array en el que vamos a asignar \n";
			$trad += $tab + $accesoArray + " //Posicion del array \n";

			if ($tipo == Tipo.DOUBLE)
			{
				$trad += $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call float64 [mscorlib]System.Double::Parse(string)\n";
			}
			else if ($tipo == Tipo.INT)
			{
				$trad += $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call int32 [mscorlib]System.Int32::Parse(string) \n";
			}
			else
			{
				$trad += $tab + "call string [mscorlib]System.Console::ReadLine() \n " + 
					$tab + "call bool [mscorlib]System.Boolean::Parse(string) \n ";
			}
			
			if (tipo != Tipo.DOUBLE)
			{
				$trad += $tab + "stelem.i4 //Cargamos el entero en el array \n";
			}
			else
			{
				$trad += $tab + "stelem.r8 //Cargamos el double en el array \n";
			}
			$maxStack = Math.max($maxStackRef+2, 3);
		}
	};


tiposimple
returns [Integer tipo, Integer cc, Integer cl] : 
	INT {$tipo = Tipo.INT; $cc = $INT.getLine(); $cl =($INT.getCharPositionInLine()+1);}
	|
	DOUBLE {$tipo = Tipo.DOUBLE; $cc = $DOUBLE.getLine(); $cl =($DOUBLE.getCharPositionInLine()+1);}
	| 
	BOOL { $tipo = Tipo.BOOLEAN; $cc = $BOOL.getLine(); $cl =($BOOL.getCharPositionInLine()+1);};

decl : 
 	tiposimple varid[$tiposimple.tipo] ( COMA varid[$tiposimple.tipo] )* PYC;

varid [Integer tipo] 
@init {Integer aridad = 0; Integer tipoAux = 0;}: 
	ID 
	{
		if (tablaSimbolos.existe ($ID.text))
		{
			mensajeError ("Error 1 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): '" + $ID.text + "' ya existe en este ambito");
		}
	}
	(CORI {aridad++;}
	(COMA {aridad++;})* 
	CORD )?
	{
		tipoAux = tablaTipos.add(tipo, aridad);
		tablaSimbolos.nuevoSimbolo ($ID.text, Simbolo.VAR ,tipoAux);
	};

expr [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	e1=eand [$tab + TABULACION]
	{
		$tipo = $e1.tipo;
		$trad = $e1.trad;	
		
		$maxStack = $e1.maxStack;
	}
	( OR
	{
		$tipo = OperaTipos.opLog(Tipo.BOOLEAN, $tipo);
		
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 4 (" + $OR.getLine() + "," + ($OR.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador logico '"+ $OR.text + "'");
		}
	}
	e2=eand [$tab + TABULACION]
	{
		$tipo = OperaTipos.opLog($e2.tipo, $tipo);
		
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 4 (" + $OR.getLine() + "," + ($OR.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador logico '"+ $OR.text + "'");
		}
		
		$trad += $e2.trad;
		$trad += $tab + "or \n";
		
		$maxStack = Math.max($maxStack, $e2.maxStack +1);
	} 
	)*;

eand	[String tab]
returns [Integer tipo, String trad, Integer maxStack] : 
	e1=erel [$tab + TABULACION]	
	{
		$tipo = $e1.tipo;
		$trad = $e1.trad;
		
		$maxStack = $e1.maxStack;
	}
	( AND 
	{
		$tipo = OperaTipos.opLog(Tipo.BOOLEAN, $tipo);
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 4 (" + $AND.getLine() + "," + ($AND.getCharPositionInLine()+1) +
					"): " +"tipo incompatible en operador logico '" + $AND.text + "'");
		}
	}
	e2=erel [$tab + TABULACION]
	{
		$tipo = OperaTipos.opLog($e2.tipo, $tipo);
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 4 (" + $AND.getLine() + "," + ($AND.getCharPositionInLine()+1) +
					"): " +"tipo incompatible en operador logico '" + $AND.text + "'");
		}
		
		$trad += $e2.trad;
		$trad += $tab + "and \n";
		
		$maxStack = Math.max($maxStack, $e2.maxStack+1);
	}
	)*;

erel [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	e1=esum	[$tab + TABULACION]
	{
		$tipo = $e1.tipo;
		$trad = $e1.trad;
		
		$maxStack = $e1.maxStack;
	}
	( RELOP 
	e2=esum [$tab + TABULACION]
	{
		String aux1="", aux2="";
		if ($e2.tipo != $tipo)
		{
			if ($e2.tipo == Tipo.DOUBLE || $tipo == Tipo.DOUBLE)
			{
				if ($e2.tipo != Tipo.DOUBLE)
				{
					aux2 = "conv.r8 //Promocion de relacional \n ";
				}
				else
				{
					aux1 = "conv.r8 //Promocion de relacional \n";
				}
			}
		}
		$tipo = OperaTipos.opRel($e2.tipo, $tipo);
		
		if ($tipo == Tipo.ERROR)
		{
			mensajeError("NO DEBERIA SALIR, TODO ES VALIDO");
			//No deberia salir, todo es valido
		}
		
		$trad += aux1;	//$tab + promociones ($e1.tipo, $tipo) + " //Promocion de e1 \n";
		
		$trad += $e2.trad;
		
		$trad += aux2; //$tab + promociones ($e2.tipo, $tipo) + " //Promocion de e2 \n";
		
		if ($RELOP.text.equals("=="))
		{
			$trad += $tab + "ceq \n";
		}
		else if ($RELOP.text.equals("!="))
		{
			$trad += $tab + "ceq \n" +
				 $tab + "ldc.i4 0 \n" +
				 $tab + "ceq \n";		
		}
		else if ($RELOP.text.equals("<"))
		{
			$trad += $tab + "clt \n";
		}
		else if ($RELOP.text.equals(">"))
		{
			$trad += $tab + "cgt \n";
		}
		else if ($RELOP.text.equals("<="))
		{
			$trad += $tab + "cgt \n" +
				 $tab + "ldc.i4 0 \n" +
				 $tab + "ceq \n";			
		}
		else if ($RELOP.text.equals(">="))
		{
			$trad += $tab + "clt \n" +
				 $tab + "ldc.i4 0 \n" +
				 $tab + "ceq \n";
		}
		
		$maxStack = Math.max($maxStack, $e2.maxStack+1);
	}
	)*;

esum [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	t1=term [$tab + TABULACION]
	{
		$tipo = $t1.tipo;
		$trad = $t1.trad;
		$maxStack = $t1.maxStack;
	}
	( ADDOP 
	{
		$tipo = OperaTipos.opAri($t1.tipo, Tipo.INT);
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 3 (" + $ADDOP.getLine() + "," + ($ADDOP.getCharPositionInLine()+1) +
				"): " +"tipo incompatible en operador aritmetico '"+ $ADDOP.text + "'");		
		}
	}
	t2=term [$tab + TABULACION]
	{
		$tipo = OperaTipos.opAri($t2.tipo, $tipo);
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 3 (" + $ADDOP.getLine() + "," + ($ADDOP.getCharPositionInLine()+1) +
					"): " +"tipo incompatible en operador aritmetico '"+ $ADDOP.text + "'");
		}
		$trad += $tab + promociones ($t1.tipo, $tipo) + " //Pomocion de t1 \n";
		
		$trad += $t2.trad;
		
		$trad += $tab + promociones ($t2.tipo, $tipo) + " //Promocion de t2 \n";
	
		if ($ADDOP.text.equals("+"))
		{
			$trad += $tab + "add \n";
		}
		else
		{
			$trad += $tab + "sub \n";
		}
		$maxStack = Math.max($maxStack, $t2.maxStack+1);
	} 
	)*;

term [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	f1=factor[$tab + "   "]
	{
		$tipo = $f1.tipo;
		$trad = $f1.trad;
		$maxStack = $f1.maxStack;
	}
	( MULOP 
	{
		$tipo = OperaTipos.opAri(Tipo.INT, $tipo);
						
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 3 (" + $MULOP.getLine() + "," + ($MULOP.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador aritmetico '" + $MULOP.text + "'");		
		}	
	}
	f2=factor [$tab + TABULACION]
	{
		$tipo = OperaTipos.opAri($f2.tipo, $tipo);
						
		if ($tipo == Tipo.ERROR)
		{
			mensajeError ("Error 3 (" + $MULOP.getLine() + "," + ($MULOP.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador aritmetico '" + $MULOP.text + "'");		
		}
		
		$trad += $tab + promociones ($f1.tipo, $tipo) + " //Promocion de f1 \n";
		
		$trad += $f2.trad;
		
		$trad += $tab + promociones ($f2.tipo, $tipo) + " //Promocion de f2 \n";
		
		if ($MULOP.text.equals("*"))
		{
			$trad += $tab + "mul \n";
		}
		else
		{
			$trad += $tab + "div \n";
		}
		$maxStack = Math.max($maxStack, $f2.maxStack+1);
	}
	)*;

factor [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	base [$tab + TABULACION]
	{
		$tipo = $base.tipo;
		$trad = $base.trad;	
		$maxStack = $base.maxStack;
	}
	| 
	NOT f1=factor [$tab + TABULACION]
	{
		if ($f1.tipo != Tipo.BOOLEAN)
		{
			mensajeError ("Error 4 (" + $NOT.getLine() + "," + ($NOT.getCharPositionInLine()+1) +
					"): " + "tipo incompatible en operador logico '" + $NOT.text + "'");
		}
		$tipo = $f1.tipo;
		$trad = $f1.trad + '\n' +
			$tab + "ldc.i4 0 \n" +
			$tab + "ceq //Comparamos con 0 para negar el valor \n";
		$maxStack = $f1.maxStack + 1;
	}
	| 
	PARI ADDOP f1=factor[$tab+TABULACION] PARD 
	{
		$trad = $f1.trad + '\n';
		if ($ADDOP.text.equals("-"))
		{
			$trad += $tab + "neg //Parentesis negado \n";
		}
		$tipo = $f1.tipo;
		$maxStack = $f1.maxStack;
			
	};

base [String tab]
returns [Integer tipo, String trad, Integer maxStack]: 
	ENTERO 
	{
		$tipo = Tipo.INT;
		$trad = $tab + "ldc.i4 " + $ENTERO.text + '\n';	
		$maxStack = 1;
	} 
	| 
	REAL  
	{
		$tipo = Tipo.DOUBLE;
		$trad = $tab + "ldc.r8 " + $REAL.text + '\n';
		$maxStack = 1;
	}
	| 
	BOOLEANO 
	{
		$tipo = Tipo.BOOLEAN;
		if ($BOOLEANO.text.equals("True"))
		{
			$trad = $tab + "ldc.i4 1 //Un True \n";
		}
		else
		{
			$trad = $tab + "ldc.i4 0 //Un False \n";
		}
		$maxStack = 1;
	}
	| 
	PARI expr [$tab+TABULACION] PARD 
	{
		$tipo = $expr.tipo;
		$trad = $tab + " // --- ( --- \n";
		$trad += $expr.trad;
		$trad += $tab + " // --- ) --- \n";	
		$maxStack = $expr.maxStack;
	}
	| 
	ref [$tab+TABULACION] 
	{
		$tipo = $ref.tipo;
		if ($ref.accesoArray.equals(""))
		{
			$trad = $tab + "ldloc " + $ref.var + " \n";
		}
		else
		{
			$trad = $tab + "ldloc " + $ref.var + " \n";
			$trad += $ref.accesoArray;
			if ($ref.tipo == Tipo.DOUBLE)
			{
				$trad += $tab + "ldelem.r8 //Acceso a un array de doubles \n";
			}
			else
			{
				$trad += $tab + "ldelem.i4 //Acceso a un array de enteros o booleanos \n";
			}
		}
		$maxStack = $ref.maxStack;
	};

ref 	[String tab]
returns [Integer tipo, Integer var, String accesoArray, Integer maxStack]
@init {Simbolo simbAux;}: 
	ID 
	{	
		simbAux = tablaSimbolos.busca($ID.text);
		if (simbAux == null)
		{
			mensajeError ("Error 2 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): simbolo '"+ $ID.text +"' no ha sido declarado");	
		}	


		$tipo = simbAux.tipo;
		
		$var = tablaSimbolos.getPos($ID.text);
		$accesoArray = "";
		$maxStack = 1;
	}
	(CORI 
	{
		if (!tablaTipos.isArray(simbAux.tipo))
		{
			mensajeError("Error 11 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): el identificador '"+ $ID.text +"' no es de tipo array");
		}
	}
	
	indices[$tab+TABULACION, simbAux.tipo, $ID.text, $CORI.getLine(), $CORI.getCharPositionInLine()+1] 
	{
		$tipo = $indices.tipoFinal;
		$accesoArray = $indices.trad + " //Acceso a un array" + '\n';
		$maxStack = 1+$indices.maxStack;
	}
	CORD)?
	{
		if (tablaTipos.isArray($tipo))
		{
			mensajeError ("Error 9 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
					"): " + "numero insuficiente de indices en el array '" + $ID.text + "'");
		}
	};

indices [String tab, Integer tipoInicial, String id, int cl, int cc]
returns [Integer tipoFinal, String trad, Integer maxStack] : 
	ex1=expr [$tab + TABULACION]
	{
		if ($ex1.tipo != Tipo.INT && $ex1.tipo != Tipo.DOUBLE)
		{
		 	mensajeError ("Error 13 (" + cl + "," + cc +
					"): " + "indice de tipo incompatible");
		}
		
		$tipoFinal = tablaTipos.desRef($tipoInicial);
		if ($tipoFinal == null)
		{
			mensajeError ("Error 12 (" + cl + "," + cc +
					"): demasiados indices");
		}
		
		$trad = $tab + $ex1.trad + " //Calculo del primer indice \n";
		
		
		
		if ($ex1.tipo == Tipo.DOUBLE)
		{
			$trad += "conv.i4 //Convertimos double a entero \n";
		}
		$maxStack = $ex1.maxStack;
	} 
	(COMA 
	ex2=expr [$tab + TABULACION]
	{
	
		if (!tablaTipos.isArray($tipoFinal))
		{
			mensajeError ("Error 12 (" + $COMA.getLine() + "," + ($COMA.getCharPositionInLine()+1) +
					"): demasiados indices");
		}
	
		$trad += $tab + "ldc.i4 " + tablaTipos.get($tipoFinal).getTamanyo().toString() + "\n";
		$trad += $tab + "mul \n";
		
		if ($ex2.tipo != Tipo.INT && $ex2.tipo != Tipo.DOUBLE)
		{
			mensajeError ("Error 13 (" + $COMA.getLine() + "," + ($COMA.getCharPositionInLine()+1) +
					"): indice de tipo incompatible");	
		}
		
		$trad += $tab + $ex2.trad + " //Calculo del siguiente indice \n";
		
		
		if ($ex2.tipo == Tipo.DOUBLE)
		{
			$trad += $tab + "conv.i4 //Convertimos double a entero \n";
		}
		
		$trad += $tab + "add \n";

		
		$tipoFinal = tablaTipos.desRef($tipoFinal);
		if ($tipoFinal == null)
		{
			mensajeError ("Error 12 (" + $COMA.getLine() + "," + ($COMA.getCharPositionInLine()+1) +
					"): demasiados indices");
		}	
		$maxStack = Math.max($maxStack, $ex2.maxStack+1);
	}	
	)*
;







CLASS		:	'class';
SINGLE		:	'Single';
VOID		:	'void';
MAIN		:	'Main';
INT		:	'int';
DOUBLE		:	'double';
BOOL		:	'bool';
PUBLIC		:	'public';
STATIC		:	'static';
IF		:	'if';
ELSE		:	'else';
FOREACH		:	'foreach';
VAR		:	'var';
IN		:	'in';
FOR		:	'for';
TO		:	'to';
STEP		:	'step';
WHILE		:	'while';
BREAK		:	'break';
CONTINUE	:	'continue';
NEW		:	'new';
WRITELINE	:	'System.Console.WriteLine';
READLINE	:	('int'|'double'|'bool')'.Parse(System.Console.ReadLine())';
LLAVEI		:	'{';
LLAVED		:	'}';
PARI		:	'(';
PARD		:	')';
CORI		:	'[';
CORD		:	']';
COMA		:	',';
PYC		:	';';
ASIG		:	'=';
OR		:	'|';
AND		:	'&';
RELOP		:	'=='|'!='|'<' | '>' | '<=' | '>=';
ADDOP		:	'+' | '-';
MULOP		:	'*' | '/';
NOT		:	'!';
PUNTO		:	'.';
ENTERO		:	('0'..'9')+;
REAL		:	('0'..'9')+'.'('0'..'9')+;
BOOLEANO	:	'True' | 'False';
ID		:	('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
COMENTARIO	:	'/*' .* '*/' {skip();};
COMENTARIO2	:	'//'~'\n'* {skip();};
NEWLINE		:		('\r'? '\n'|' '|'\t')+  {skip();}; 

